<!DOCTYPE html>
<html>
<!--
Copyright 2008 The Closure Library Authors. All Rights Reserved.

Use of this source code is governed by the Apache License, Version 2.0.
See the COPYING file for details.
-->
  <!--
  -->
  <head>
<meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>Closure Unit Tests - CSS Object Model helper</title>
    <script src="../base.js"></script>
    <script>
      goog.require('goog.array');
      goog.require('goog.cssom');
      goog.require('goog.testing.jsunit');
      goog.require('goog.userAgent');
    </script>
  </head>

  <link rel="stylesheet" type="text/css" href="cssom_test_link_1.css">
  <style>
    /* This will import css_test_import_1 and css_test_import_2 */
    @import "cssom_test_import_1.css?cachebust=1";

    .css-style-1 {
      display: block;
    }
    .css-style-2 {
      display: block;
    }
    .css-style-3 {
      display: block;
    }
  </style>
  <body>

    <script>

      // Since sheet cssom_test1.css's first line is to import
      // cssom_test2.css, we should get 2 before one in the string.
      var cssText = '.css-link-1 { display: block; } ' +
          '.css-import-2 { display: block; } ' +
          '.css-import-1 { display: block; } ' +
          '.css-style-1 { display: block; } ' +
          '.css-style-2 { display: block; } ' +
          '.css-style-3 { display: block; }';

      var replacementCssText = '.css-repl-1 { display: block; }';

      var isIe7 = goog.userAgent.IE &&
                 (goog.userAgent.compare(goog.userAgent.VERSION, '7.0') == 0);

      // We're going to toLowerCase cssText before testing, because IE returns
      // CSS property names in UPPERCASE, and the function shouldn't
      // "fix" the text as it would be expensive and rarely of use.
      // Same goes for the trailing whitespace in IE.
      // Same goes for fixing the optimized removal of trailing ; in rules.
      // Also needed for Opera.
      function fixCssTextForIe(cssText) {
        cssText = cssText.toLowerCase().replace(/\s*$/, '');
        if (cssText.match(/[^;] \}/)) {
          cssText = cssText.replace(/([^;]) \}/g, '$1; }');
        }
        return cssText;
      }

      function testGetFileNameFromStyleSheet() {
        var styleSheet = {'href': 'http://foo.com/something/filename.css'};
        assertEquals('filename.css',
            goog.cssom.getFileNameFromStyleSheet(styleSheet));

        styleSheet = {'href': 'https://foo.com:123/something/filename.css'};
        assertEquals('filename.css',
            goog.cssom.getFileNameFromStyleSheet(styleSheet));

        styleSheet = {'href': 'http://foo.com/something/filename.css?bar=bas'};
        assertEquals('filename.css',
            goog.cssom.getFileNameFromStyleSheet(styleSheet));

        styleSheet = {'href': 'filename.css?bar=bas'};
        assertEquals('filename.css',
            goog.cssom.getFileNameFromStyleSheet(styleSheet));

        styleSheet = {'href': 'filename.css'};
        assertEquals('filename.css',
            goog.cssom.getFileNameFromStyleSheet(styleSheet));
      }

      function testGetAllCssStyleSheets() {
        var styleSheets = goog.cssom.getAllCssStyleSheets();
        assertEquals(4, styleSheets.length);
        // Makes sure they're in the right cascade order.
        assertEquals('cssom_test_link_1.css',
            goog.cssom.getFileNameFromStyleSheet(styleSheets[0]));
        assertEquals('cssom_test_import_2.css',
            goog.cssom.getFileNameFromStyleSheet(styleSheets[1]));
        assertEquals('cssom_test_import_1.css',
            goog.cssom.getFileNameFromStyleSheet(styleSheets[2]));
        // Not an external styleSheet
        assertNull(goog.cssom.getFileNameFromStyleSheet(styleSheets[3]));
      }

      function testGetAllCssText() {
        var allCssText = goog.cssom.getAllCssText();
        // In IE7, a CSSRule object gets included twice and replaces another
        // existing CSSRule object. We aren't using
        // goog.testing.ExpectedFailures since it brings in additional CSS
        // which breaks a lot of our expectations about the number of rules
        // present in a style sheet.
        if (!isIe7) {
          assertEquals(cssText, fixCssTextForIe(allCssText));
        }
      }

      function testGetAllCssStyleRules() {
        var allCssRules = goog.cssom.getAllCssStyleRules();
        assertEquals(6, allCssRules.length);
      }


      function testAddCssText() {
        var newCssText = '.css-add-1 { display: block; }';
        var newCssNode = goog.cssom.addCssText(newCssText);

        assertEquals(document.styleSheets.length, 3);

        var allCssText = goog.cssom.getAllCssText();

        // In IE7, a CSSRule object gets included twice and replaces another
        // existing CSSRule object. We aren't using
        // goog.testing.ExpectedFailures since it brings in additional CSS
        // which breaks a lot of our expectations about the number of rules
        // present in a style sheet.
        if (!isIe7) {
          // Opera inserts the CSSRule to the first position. And fixCssText
          // is also needed to clean up whitespace.
          if (goog.userAgent.OPERA) {
            assertEquals(newCssText + ' ' + cssText,
                         fixCssTextForIe(allCssText));
          } else {
            assertEquals(cssText + ' ' + newCssText,
                         fixCssTextForIe(allCssText));
          }
        }

        var cssRules = goog.cssom.getAllCssStyleRules();
        assertEquals(7, cssRules.length);

        // Remove the new stylesheet now so it doesn't interfere with other
        // tests.
        newCssNode.parentNode.removeChild(newCssNode);
        // Sanity check.
        cssRules = goog.cssom.getAllCssStyleRules();
        assertEquals(6, cssRules.length);
      }

      function testAddCssRule() {
        // test that addCssRule correctly adds the rule to the style
        // sheet.
        var styleSheets = goog.cssom.getAllCssStyleSheets();
        var styleSheet = styleSheets[3];
        var newCssRule = '.css-addCssRule { display: block; }';
        var rules = styleSheet.rules || styleSheet.cssRules;
        var origNumberOfRules = rules.length;

        goog.cssom.addCssRule(styleSheet, newCssRule, 1);

        rules = styleSheet.rules || styleSheet.cssRules;
        var newNumberOfRules = rules.length;
        assertEquals(newNumberOfRules, origNumberOfRules + 1);

        // Remove the added rule so we don't mess up other tests.
        goog.cssom.removeCssRule(styleSheet, 1);
      }

      function testAddCssRuleAtPos() {
        // test that addCssRule correctly adds the rule to the style
        // sheet at the specified position.
        var styleSheets = goog.cssom.getAllCssStyleSheets();
        var styleSheet = styleSheets[3];
        var newCssRule = '.css-addCssRulePos { display: block; }';
        var rules = styleSheet.rules || styleSheet.cssRules;
        var origNumberOfRules = rules.length;

        // Firefox croaks if we try to insert a CSSRule at an index that
        // contains a CSSImport Rule. Since we deal only with CSSStyleRule
        // objects, we find the first CSSStyleRule and return its index.
        //
        // NOTE(user): We could have unified the code block below for all
        // browsers but IE6 horribly mangled up the stylesheet by creating
        // duplicate instances of a rule when removeCssRule was invoked
        // just after addCssRule with the looping construct in. This is
        // perfectly fine since IE's styleSheet.rules does not contain
        // references to anything but CSSStyleRules.
        var pos = 0;
        if (!goog.userAgent.IE) {
          pos = goog.array.findIndex(rules, function(rule) {
            return rule.type == goog.cssom.CssRuleType.STYLE;
          });
        }
        goog.cssom.addCssRule(styleSheet, newCssRule, pos);

        rules = styleSheet.rules || styleSheet.cssRules;
        var newNumberOfRules = rules.length;
        assertEquals(newNumberOfRules, origNumberOfRules + 1);

        // Remove the added rule so we don't mess up other tests.
        goog.cssom.removeCssRule(styleSheet, pos);

        rules = styleSheet.rules || styleSheet.cssRules;
        assertEquals(origNumberOfRules, rules.length);
      }

      function testAddCssRuleNoIndex() {
        // How well do we handle cases where the optional index is
        //  not passed in?
        var styleSheets = goog.cssom.getAllCssStyleSheets();
        var styleSheet = styleSheets[3];
        var rules = styleSheet.rules || styleSheet.cssRules;
        var origNumberOfRules = rules.length;
        var newCssRule = '.css-addCssRuleNoIndex { display: block; }';

        // Try inserting the rule without specifying an index.
        // Make sure we don't throw an exception, and that we added
        // the entry.
        goog.cssom.addCssRule(styleSheet, newCssRule);

        rules = styleSheet.rules || styleSheet.cssRules;
        var newNumberOfRules = rules.length;
        assertEquals(newNumberOfRules, origNumberOfRules + 1);

        // Remove the added rule so we don't mess up the other tests.
        goog.cssom.removeCssRule(styleSheet, newNumberOfRules - 1);

        rules = styleSheet.rules || styleSheet.cssRules;
        assertEquals(origNumberOfRules, rules.length);
      }


      function testGetParentStyleSheetAfterGetAllCssStyleRules() {
        var cssRules = goog.cssom.getAllCssStyleRules();
        var cssRule = cssRules[4];
        var parentStyleSheet = goog.cssom.getParentStyleSheet(cssRule);
        var styleSheets = goog.cssom.getAllCssStyleSheets();
        var styleSheet = styleSheets[3];
        assertEquals(styleSheet, parentStyleSheet);
      }

      function testGetCssRuleIndexInParentStyleSheetAfterGetAllCssStyleRules() {
        var cssRules = goog.cssom.getAllCssStyleRules();
        var cssRule = cssRules[4];
        // Note here that this is correct - IE's styleSheet.rules does not
        // contain references to anything but CSSStyleRules while FF and others
        // include anything that inherits from the CSSRule interface.
        // See http://dev.w3.org/csswg/cssom/#cssrule.
        var ruleIndex = goog.userAgent.IE ? 1 : 2;
        assertEquals(ruleIndex,
            goog.cssom.getCssRuleIndexInParentStyleSheet(cssRule));
      }

      function testGetCssRuleIndexInParentStyleSheetNonStyleRule() {
        // IE's styleSheet.rules only contain CSSStyleRules.
        if (!goog.userAgent.IE) {
          var styleSheets = goog.cssom.getAllCssStyleSheets();
          var styleSheet = styleSheets[3];
          var newCssRule = '@media print { .css-nonStyle { display: block; } }';
          goog.cssom.addCssRule(styleSheet, newCssRule);
          var rules = styleSheet.rules || styleSheet.cssRules;
          var cssRule = rules[rules.length - 1];
          assertEquals(goog.cssom.CssRuleType.MEDIA, cssRule.type);
          // Make sure we don't throw an exception.
          goog.cssom.getCssRuleIndexInParentStyleSheet(cssRule, styleSheet);
          // Remove the added rule.
          goog.cssom.removeCssRule(styleSheet, rules.length - 1);
        }
      }

      // Tests the scenario where we have a known stylesheet and index.
      function testReplaceCssRuleWithStyleSheetAndIndex() {
        var styleSheets = goog.cssom.getAllCssStyleSheets();
        var styleSheet = styleSheets[3];
        var rules = goog.cssom.getCssRulesFromStyleSheet(styleSheet);
        var index = 2;
        var origCssRule = rules[index];
        var origCssText =
            fixCssTextForIe(goog.cssom.getCssTextFromCssRule(origCssRule));

        goog.cssom.replaceCssRule(origCssRule, replacementCssText, styleSheet,
            index);

        var rules = goog.cssom.getCssRulesFromStyleSheet(styleSheet);
        var newCssRule = rules[index];
        var newCssText = goog.cssom.getCssTextFromCssRule(newCssRule);
        assertEquals(replacementCssText, fixCssTextForIe(newCssText));

        // Now we need to re-replace our rule, to preserve parity for the other
        // tests.
        goog.cssom.replaceCssRule(newCssRule, origCssText, styleSheet, index);
        var rules = goog.cssom.getCssRulesFromStyleSheet(styleSheet);
        var nowCssRule = rules[index];
        var nowCssText = goog.cssom.getCssTextFromCssRule(nowCssRule);
        assertEquals(origCssText, fixCssTextForIe(nowCssText));
      }

      function testReplaceCssRuleUsingGetAllCssStyleRules() {
        var cssRules = goog.cssom.getAllCssStyleRules();
        var origCssRule = cssRules[4];
        var origCssText =
            fixCssTextForIe(goog.cssom.getCssTextFromCssRule(origCssRule));
        // notice we don't pass in the stylesheet or index.
        goog.cssom.replaceCssRule(origCssRule, replacementCssText);

        var styleSheets = goog.cssom.getAllCssStyleSheets();
        var styleSheet = styleSheets[3];
        var rules = goog.cssom.getCssRulesFromStyleSheet(styleSheet);
        var index = goog.userAgent.IE ? 1 : 2;
        var newCssRule = rules[index];
        var newCssText =
            fixCssTextForIe(goog.cssom.getCssTextFromCssRule(newCssRule));
        assertEquals(replacementCssText, newCssText);

        // try getting it the other way around too.
        var cssRules = goog.cssom.getAllCssStyleRules();
        var newCssRule = cssRules[4];
        var newCssText =
            fixCssTextForIe(goog.cssom.getCssTextFromCssRule(newCssRule));
        assertEquals(replacementCssText, newCssText);

        // Now we need to re-replace our rule, to preserve parity for the other
        // tests.
        goog.cssom.replaceCssRule(newCssRule, origCssText);
        var cssRules = goog.cssom.getAllCssStyleRules();
        var nowCssRule = cssRules[4];
        var nowCssText =
            fixCssTextForIe(goog.cssom.getCssTextFromCssRule(nowCssRule));
        assertEquals(origCssText, nowCssText);
      }
    </script>
    </body>
</html>
